可以这样类比来想:think of like you phone is slowing down after you install too many apps, you need to find out which app is using the resourses and is making you phone slow.
1、we can use chrome devtools, and in the performance tab, and see what is taking the most time.
2、we can use react devtools chrome extension, we can track unneccessary rerenders or infinite loop of rerenders.
3、we can use React.memo and useCallback to making app fast and tracking where is slow.
4、I will check if there are any large amount of data to render at once. usually we render only the visible amount of data, use packages like react.window or react.virtulized.
5、avoid inline functions and objects。
Inline functions and objects are recreated on every render, which can cause child components to re-render unnecessarily — especially if those children depend on referential equality (React.memo, dependency arrays, etc.).
Example problem:
xxxxxxxxxx11<Child onClick={() => handleClick()} data={{ foo: 1 }} />
Every render:
() => handleClick() is a new function{ foo: 1 } is a new objectSo Child thinks props changed → re-renders every time, even if nothing actually changed.
How to fix it:
①Use useCallback for functions
xxxxxxxxxx31const handleClick = useCallback(() => {2// logic3}, []);
②Use useMemo for objects/arrays
xxxxxxxxxx11const data = useMemo(() => ({ foo: 1 }), []);
上面的步骤要理解,然后简要的答案先说出来:
“I first profile the app using React DevTools to identify which components became slow. Then I check for unnecessary re-renders, expensive operations in render, problematic effects, or network calls happening too frequently.
“I also check if I accidentally introduced inline functions or inline objects. Those get recreated on every render, which can break memoization and cause unnecessary re-renders. If I see that pattern, I move them into
useCallbackoruseMemoto keep stable references.”Once I find the root cause, I optimize it—using memoization, moving state, cleaning up effects, or applying code splitting. My approach is always: measure → identify → fix → re-measure.”
The general idea is:If the user is already authenticated, redirect them away from the login page.
You check the auth state, and if the user is logged in, redirect them:
xxxxxxxxxx141import { Navigate } from "react-router-dom";2import { useAuth } from "./authContext";34function LoginPage() {5 const { user } = useAuth();67 if (user) {8 return <Navigate to="/dashboard" replace />;9 }1011 return <LoginForm />;12}1314export default LoginPage;Explanation:
user exists → redirect to dashboardThis ensures the login page is never visible to authenticated users.
Sometimes I create a wrapper:
xxxxxxxxxx51function PublicOnlyRoute({ children }) {2 const { user } = useAuth();34 return user ? <Navigate to="/dashboard" replace /> : children;5}Then wrap the route:
xxxxxxxxxx81<Route2 path="/login"3 element={4 <PublicOnlyRoute>5 <LoginPage />6 </PublicOnlyRoute>7 }8/>If the backend renders the page, the server can also redirect if the session exists.
理解上面的内容后,简要回答:
“I prevent the login page from being accessible after login by checking whether the user is authenticated. If the user is already logged in, I immediately redirect them to a protected page like the dashboard. In React Router, this usually means returning a
<Navigate>component whenuserexists. This ensures the login page is only visible to unauthenticated users.”
Users should never see raw server errors like “500 Internal Server Error”.
Example:
This keeps the UI clean and understandable.
In React components or services:
xxxxxxxxxx71try {2 const res = await fetch("/api/data");3 if (!res.ok) throw new Error("Request failed");4 return await res.json();5} catch (err) {6 setError(err.message);7}Then the UI reacts to that error state.
For example:
xxxxxxxxxx61{error && (2 <div>3 <p>{error}</p>4 <button onClick={retry}>Try Again</button>5 </div>6)}I often show different messages depending on the error:
This makes the UI feel professional and predictable.
React Query makes this much easier:
xxxxxxxxxx71useQuery({2 queryKey: ["data"],3 queryFn: fetchData,4 onError: (err) => {5 toast.error(err.message);6 }7});React Query automatically gives:
error stateisError booleanSo the UI can handle errors more gracefully.
理解上面之后,简要的回答:
“I handle API errors by wrapping network calls in
try/catchor using libraries like React Query / Axios, capturing both HTTP-error responses and network exceptions. I store the error state (e.g. usinguseStateor React Query’serror/isErrorflags), and show a user-friendly fallback UI — such as an error message, a toast/notification, or a retry button wrapped in a conditional render. For critical component errors I can also use a global error boundary (via ErrorBoundary) to catch rendering or lifecycle exceptions so the whole UI doesn’t break. For HTTP status codes I differentiate behaviors — e.g. 401 leads to redirect to login, 403 shows “access denied”, 400 shows validation feedback. I may also implement retry logic or exponential-backoff on failures (especially for transient network errors) and log errors (to console or a logging service) for debugging.”
useEffect dependency arrayOne of the most common causes is a missing or incorrect dependency array:
❌ Bad (fetch runs every render)
xxxxxxxxxx31useEffect(() => {2 fetchData();3});✅ Good (runs only once on mount)
xxxxxxxxxx31useEffect(() => {2 fetchData();3}, []);Or if dependencies are required, ensure they don’t change on every render.
useCallbackIf the fetch function is declared inline and passed to children or used inside effects, it may be recreated every render, causing repeated calls.
❌ Bad
xxxxxxxxxx41useEffect(() => {2 fetchData();3}, [fetchData]);4const fetchData = () => { }; // new function per render✅ Good
xxxxxxxxxx31const fetchData = useCallback(() => {2 // call API3}, []);useMemoIf parameters for your fetch are created inline (like an object), the reference changes every time:
❌
xxxxxxxxxx31useEffect(() => {2 fetchData(params);3}, [{ id: 1 }]);✔️
xxxxxxxxxx11const params = useMemo(() => ({ id: 1 }), []);Sometimes multiple sibling components fetch the same data.
Solution:
This way the data is fetched once and shared.
Tools like React Query or Redux Toolkit Query automatically:
React Query example:
xxxxxxxxxx41const { data } = useQuery({2 queryKey: ["user", id],3 queryFn: fetchUser,4});Duplicate calls are automatically prevented.
Some developers accidentally remount a component repeatedly:
xxxxxxxxxx11{isOpen && <UserList />}If isOpen toggles repeatedly, the component unmounts and remounts, triggering useEffect again.
Fix:
理解上面的内容后,简要的回答:
“First, I check the
useEffectdependency array — missing or unstable dependencies often cause repeated fetch calls. Then I make sure the fetch function is memoized withuseCallbackand any params withuseMemo, so they don’t change every render. If several components need the same data, I move the fetching logic into a global store, such as React Context or Redux, so the data is fetched once and reused everywhere. For even more control, I use libraries like React Query or Redux Toolkit Query, which provide request deduplication, caching, and stale-while-revalidate behavior out of the box. In short: stabilize dependencies, avoid remounting, and centralize or cache the data so it only gets fetched once.”
可以这样想:think of this like you are travelling, you don't take your complete cupboard or wardrobe with you, you take neccessary some part of it in the travel.
Just like that, whenever you writing thousands of files in your app, you don't need to ship everything to the production.So you need to bundle it and organize it somehow so that it will take minimum size on the user's device and it will be efficient for the performance.
理解了上面的内容之后,简要回答:
“I usually optimize bundle size by focusing on code splitting, dependency management, and build-time optimizations. First, I implement route-level code splitting using
React.lazyandSuspense, so the app only loads what’s needed for the current page. For heavier features, I use dynamic imports or component-level lazy loading to avoid shipping large chunks upfront. I also regularly analyze the bundle using tools like Webpack Bundle Analyzer or Vite’s analyze plugin to identify large or unused dependencies. If a library is too heavy, I try to replace it with a smaller alternative, remove unused code, or tree-shake it. On the build side, I make sure features like tree-shaking, minification, and dead-code elimination are enabled, and I avoid importing entire libraries when I only need specific modules. For assets, I optimize images, use*.svgcomponents instead of PNGs, and rely on CDNs where possible. Overall, the approach is: split the code, trim unnecessary dependencies, enable build optimizations, and continuously analyze the bundle to keep it lean.”
当面试官问large forms,他们想要看到你是否能够优化form、维持状态的能力。
这是Shubham Kulkarni | Coding and Interviews这位老师的回答要点。
1、use form management library like formik or react hook form,
2、structure the form to logical steps or sections.
3、use controlled components,
4、use zod library to validate.
下面是chatgpt的回答:
✅ 1. Group related fields into logical sections
Don’t treat 20 fields as 20 individual states. Instead, think of your form as sections:
This gives you:
✔ Better UI/UX ✔ Smaller components ✔ Cleaner validation
Example structure:
xxxxxxxxxx51<FormProvider {...methods}>2<PersonalInfoSection />3<ContactSection />4<PreferencesSection />5</FormProvider>
✅ 2. Use a form library — don’t manage 20 states manually
It handles:
For 20+ fields, RHF is dramatically better than useState or even Formik.
Example:
xxxxxxxxxx41const methods = useForm({2defaultValues: { ...20 fields here... },3mode: "onSubmit",4});
✅ 3. Store all form values in one place (not 20 states)
For large forms, do NOT use:
xxxxxxxxxx31const [name, setName] = useState('');2const [email, setEmail] = useState('');3...
Instead:
xxxxxxxxxx81const methods = useForm({2defaultValues: {3name: '',4email: '',5age: '',6// ...7}8});
Or if using useState manually:
xxxxxxxxxx11const [form, setForm] = useState({...});
✅ 4. Extract repeating field logic into reusable components
If many fields share:
Create a component:
xxxxxxxxxx91function FormInput({ name, label, register, errors }) {2return (3<div>4<label>{label}</label>5<input {...register(name)} />6{errors[name] && <p>{errors[name].message}</p>}7</div>8);9}
Now your form becomes:
xxxxxxxxxx31<FormInput name="email" label="Email" />2<FormInput name="phone" label="Phone" />3<FormInput name="age" label="Age" />
✅ 5. Use a schema validator (Zod/Yup) to avoid manual checks
With 20+ fields, writing if (!name) error... everywhere becomes painful.
Use Zod:
xxxxxxxxxx61const schema = z.object({2name: z.string().min(1),3email: z.string().email(),4phone: z.string().min(10),5// 20 more...6});
Integrate with RHF:
xxxxxxxxxx31const methods = useForm({2resolver: zodResolver(schema)3});
Now all validation is centralized and reusable.
✅ 6. Use dynamic generation (optional but powerful)
If fields share patterns, define them as config:
xxxxxxxxxx61const fieldConfig = [2{ name: "email", label: "Email" },3{ name: "phone", label: "Phone" },4{ name: "address", label: "Address" },5// ...6];
Render automatically:
xxxxxxxxxx31{fieldConfig.map((f) => (2<FormInput key={f.name} {...f} />3))}
Great for 20–100 fields.
✅ 7. Split the form into tabs or steps if possible
For UX improvement:
Use a stepper or wizard.
Better:
✔ Less intimidating ✔ Faster rendering ✔ Validation per step
✅ 8. Memoize heavy components
For example, expensive dropdowns.
xxxxxxxxxx31const CountrySelect = memo(function CountrySelect() {2return <select>...</select>;3});
RHF already minimizes re-renders, but memoization helps further.
🧪 9. For huge forms → Use RHF + Suspense + lazy-loaded sections
If your sections are large:
xxxxxxxxxx11const WorkInfo = lazy(() => import("./WorkInfo"));
Better performance for enterprise apps.
🎯 Final recommended stack for 20+ fields
| Layer | Recommendation |
|---|---|
| Form library | React Hook Form |
| Validation | Zod |
| Structure | Multiple components (sections) |
| Extra | Config-driven generation for repeated patterns |
“For a form with 20 or more fields, I try to handle it efficiently by combining structure, reusable components, and proper form libraries. First, I group related fields into sections or components to keep the UI organized and maintainable.
Then, I use a form library like React Hook Form, which manages all field states centrally and minimizes re-renders, instead of using separate
useStatefor each field. For validation, I rely on schema-based validators like Zod or Yup so that all rules are centralized and type-safe. I also extract repeated field patterns into reusable input components, and optionally generate fields dynamically from a config object. For very large forms, I consider splitting the form into steps or tabs to improve UX and performance.Overall, my approach is: centralized state, reusable components, schema validation, and structured sections to handle large forms efficiently.”
面试管想听到的要点就是:the theme state should be global, so whenever you toggle the theme at the global state, it will be auto reflected in the children level.
1、I will check whether the dark mode is user preference based or system based.
2、if it's system based.I will use prefers-color-scheme media query to detect system preference on first load.then set the theme mode.
3、if it's user mode, I often rely on react context or redux to set global state, and rely on CSS variables or tailwindcss to set dark mode.
简要回答:
“To implement dark mode in a React app, I usually manage a theme state at the top level, using either React Context, Redux, or a global store like Zustand. I store the user’s preference in
localStorageso it persists across sessions, and I use a toggle button in the UI to switch between light and dark themes. For styling, I often rely on CSS variables or a utility framework like Tailwind CSS, which supports dark mode with thedark:variant. Components then automatically adapt by referencing the theme variables, and I can also add a class likedarkon the root element to switch the entire app. In short, the approach is: store theme in global state, persist the preference, and style components using CSS variables or a framework that supports dark mode
XSS (Cross-Site Scripting)
Frontend apps
为什么会发生 XSS?
前端可以做什么?
可用工具 / 方法?
{userInput}dangerouslySetInnerHTML + DOMPurifyeval()“To prevent XSS attacks in frontend apps, I follow a combination of output encoding, input sanitization, and safe rendering practices. First, I never inject raw HTML into the DOM. For example, in React, I avoid using
dangerouslySetInnerHTMLunless absolutely necessary, and if I do, I sanitize the content first using libraries like DOMPurify. Second, I validate and sanitize any user input on both the client and the server, especially if it will be displayed later. Third, I rely on the framework’s built-in protections—React automatically escapes content rendered inside JSX, so normal expressions like{userInput}are safe by default. Finally, I also follow general best practices: avoid usingeval(), ensure third-party scripts are trusted, and apply a strong Content Security Policy (CSP) at the server level to prevent inline script execution. Overall, the approach is: sanitize inputs, escape outputs, avoid raw HTML, and leverage framework protections to prevent XSS.”
1️⃣ 题目关键词
Offline/Online state
Gracefully
React
Service Workers
2️⃣ 拆解后的思路
你可以把题目拆解成三个小问题:
(A) 如何在 React 中检测在线/离线状态?
用浏览器 API:
xxxxxxxxxx31navigator.onLine // 当前是否在线2window.addEventListener('online', callback)3window.addEventListener('offline', callback)在 React 里可以用 useState + useEffect 或 Context 来存储全局状态。
UI 上可以显示提示:比如顶部横幅“你已经离线”。
(B) 如何用 Service Worker 支持离线功能?
Service Worker 可以缓存静态资源和 API 数据。
核心点:
推荐使用库:Workbox 或 PWA setup(Create React App 默认支持)。
(C) 如何在重新上线时同步数据?
3️⃣ 组合成回答框架
navigator.onLine + online/offline 事件老师的要点:
1、I will use navigator.onLine API provided by the browser, to detect network status. And listen to the online and offline events.
2、use service workers to cache critical assets and API responses.
“To handle offline and online states in a React app, I usually combine browser APIs with Service Workers.
First, I detect network status using the
navigator.onLineproperty and listen to theonlineandofflineevents, updating a global state or React Context so components can react accordingly. For example, I can show a banner or toast to inform users they are offline.
Second, I use a Service Worker—for example, via Workbox or a PWA setup—to cache critical assets and API responses. This allows the app to function in offline mode and serve cached content.
For data syncing, when the user goes back online, I can implement background sync to send queued updates to the server.
In short, my approach is: listen to network events, update UI reactively, cache resources with a Service Worker, and optionally queue requests for later syncing to provide a seamless offline/online experience.”
Production is not getting the latest update
Unless in incognito
为什么普通用户看到旧版本?
为什么隐身模式正常?
如何解决?
简要回答:
“This is usually a caching issue. When users see the old version in production but incognito works, it means the browser or a service worker is serving cached files. To fix this, I would:
- Use content hashing for JS/CSS filenames in the build process, so each new deployment has unique filenames and browsers fetch the new files automatically.
- Ensure HTTP cache headers are correctly set, like
Cache-Control: no-cacheor shortmax-agefor index.html, so the HTML always checks for new bundles.- If using a Service Worker, implement cache versioning and update logic, so the new assets replace the old cached ones. In short, the solution is to manage caching properly through content hashing, cache headers, and service worker update strategy, so users always get the latest version without relying on incognito mode.”